package com.ruoyi.common.utils;

import cn.hutool.core.convert.Convert;
import org.apache.commons.lang3.time.DateFormatUtils;

import java.lang.management.ManagementFactory;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.time.temporal.TemporalAdjusters;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 时间工具类
 * 
 * @author ruoyi
 */
public class DateUtils extends org.apache.commons.lang3.time.DateUtils
{
    public static String YYYY = "yyyy";

    public static String YYYY_MM = "yyyy-MM";

    public static String MM_DD = "MM/dd";

    public static String YYYY_MM_DD = "yyyy-MM-dd";

    public static String YYYYMMDDHHMMSS = "yyyyMMddHHmmss";

    public static String YYYY_MM_DD_HH_MM_SS = "yyyy-MM-dd HH:mm:ss";

    private static String[] parsePatterns = {
            "yyyy-MM-dd", "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm", "yyyy-MM", 
            "yyyy/MM/dd", "yyyy/MM/dd HH:mm:ss", "yyyy/MM/dd HH:mm", "yyyy/MM",
            "yyyy.MM.dd", "yyyy.MM.dd HH:mm:ss", "yyyy.MM.dd HH:mm", "yyyy.MM"};

    /**
     * 获取当前Date型日期
     */
    public static final Date getNowDate() { return new Date(); }
    /**
     * 获取当前日期, 默认格式为yyyy-MM-dd
     */
    public static final String getDate() {  return dateTimeNow(YYYY_MM_DD);    }
    /**
     * 获取当前日期, 默认格式为yyyy-MM-dd HH:mm:ss
     */
    public static final String getTime()
    {
        return dateTimeNow(YYYY_MM_DD_HH_MM_SS);
    }
    /**
     * 获取当前日期, 默认格式为yyyyMMddHHmmss
     */
    public static final String dateTimeNow()
    {
        return dateTimeNow(YYYYMMDDHHMMSS);
    }
    /**
     * 获取当前日期, 为传入format格式
     */
    public static final String dateTimeNow(final String format) { return parseDateToStr(format, new Date()); }
    /**
     * 获取传入日期, 默认格式为yyyy-MM-dd
     */
    public static final String dateTime(final Date date) { return parseDateToStr(YYYY_MM_DD, date); }
    /**
     * 传入日期Date转为String类型格式为传入的format格式
     */
    public static final String parseDateToStr(final String format, final Date date) { return new SimpleDateFormat(format).format(date); }
    /**
     * 传入日期字符串转为Date类型格式为传入的format格式
     */
    public static final Date dateTime(final String format, final String ts) {
        try {
            return new SimpleDateFormat(format).parse(ts);
        } catch (ParseException e) {
            throw new RuntimeException(e);
        }
    }
    /**
     * 获取当前日期 即年/月/日 如2018/08/08
     */
    public static final String datePath() {
        return DateFormatUtils.format(new Date(), "yyyy/MM/dd");
    }
    /**
     * 获取传入日期 即年/月/日 如2018/08/08
     */
    public static final String datePath(Date date)
    {
        return DateFormatUtils.format(date, "yyyy/MM/dd");
    }
    /**
     * 获取当前日期 即年/月/日 如20180808
     */
    public static final String dateTime() {
        return DateFormatUtils.format(new Date(), "yyyyMMdd");
    }
    /**
     * 日期型字符串转化为日期 格式 参数：Object
     */
    public static final Date parseDate(Object str) {
        try {
            return parseDate(str.toString(), parsePatterns);
        } catch (ParseException e) {
            return null;
        }
    }
    /**
     * 获取服务器启动时间
     */
    public static Date getServerStartDate() {
        long time = ManagementFactory.getRuntimeMXBean().getStartTime();
        return new Date(time);
    }
    /**
     * 计算相差天数
     */
    public static int differentDaysByMillisecond(Date date1, Date date2) {
        return Math.abs((int) ((date2.getTime() - date1.getTime()) / (1000 * 3600 * 24)));
    }
    /**
     * 计算两个时间差
     */
    public static String getDatePoor(Date endDate, Date nowDate) {
        long nd = 1000 * 24 * 60 * 60;
        long nh = 1000 * 60 * 60;
        long nm = 1000 * 60;
        // long ns = 1000;
        // 获得两个时间的毫秒时间差异
        long diff = endDate.getTime() - nowDate.getTime();
        // 计算差多少天
        long day = diff / nd;
        // 计算差多少小时
        long hour = diff % nd / nh;
        // 计算差多少分钟
        long min = diff % nd % nh / nm;
        // 计算差多少秒//输出结果
        // long sec = diff % nd % nh % nm / ns;
        return day + "天" + hour + "小时" + min + "分钟";
    }
    /**
     * 增加 LocalDateTime ==> Date
     */
    public static Date toDate(LocalDateTime temporalAccessor)
    {
        ZonedDateTime zdt = temporalAccessor.atZone(ZoneId.systemDefault());
        return Date.from(zdt.toInstant());
    }
    /**
     * 增加 LocalDate ==> Date
     */
    public static Date toDate(LocalDate temporalAccessor)
    {
        LocalDateTime localDateTime = LocalDateTime.of(temporalAccessor, LocalTime.of(0, 0, 0));
        ZonedDateTime zdt = localDateTime.atZone(ZoneId.systemDefault());
        return Date.from(zdt.toInstant());
    }
    /**
     * 将分钟数转换为"HH:MM"格式的时间字符串
     * 例如：90分钟将转换为"01:30"
     */
    public static String convertMinutesToHHMM(int minutes) {
        Duration duration = Duration.ofMinutes(minutes);
        long hours = duration.toHours();
        long mins = duration.toMinutes() % 60;
        // 格式化确保小时和分钟均为两位数字
        return String.format("%02d:%02d", hours, mins);
    }
    /**
     * 计算指定时间加上分钟数后的时间，并格式化为"HH:MM"
     */
    public static String calculateTimeAfterMinutes(Date dateTime, int minutes) {
        SimpleDateFormat outputFormat = new SimpleDateFormat("HH:mm");
        // 使用Calendar进行时间计算
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(dateTime);
        // 加上分钟
        calendar.add(Calendar.MINUTE, minutes);
        // 格式化输出HH:MM
        return outputFormat.format(calendar.getTime());
    }
    /**
     * 指定时间格式化为"HH:MM"
     */
    public static String convertDateTimeToHHMM(Date dateTime) {
        SimpleDateFormat outputFormat = new SimpleDateFormat("HH:mm");
        // 使用Calendar进行时间计算
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(dateTime);
        // 格式化输出HH:MM
        return outputFormat.format(calendar.getTime());
    }
    /**
     * 指定时间格式化为"HH:MM"
     */
    public static String convertDateTimeToChineseHHMM(Date dateTime) {
        SimpleDateFormat outputFormat = new SimpleDateFormat("HH时mm分");
        // 使用Calendar进行时间计算
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(dateTime);
        // 格式化输出HH:MM
        return outputFormat.format(calendar.getTime());
    }
    /**
     * 将"HH:MM"格式的时间字符串转换为"X小时Y分"的中文表示
     * 例如："01:30"将转换为"1小时30分"
     */
    public static String convertHHMMToChinese(String sleepDuration) {
        // 使用SimpleDateFormat解析时间
        SimpleDateFormat sdf = new SimpleDateFormat("HH:mm");
        try {
            Date time = sdf.parse(sleepDuration);
            // 使用Calendar获取小时和分钟
            Calendar calendar = Calendar.getInstance();
            calendar.setTime(time);
            int hours = calendar.get(Calendar.HOUR_OF_DAY);
            int minutes = calendar.get(Calendar.MINUTE);
            // 输出结果
            return (hours + "小时" + minutes + "分");
        } catch (ParseException e) {
            throw new RuntimeException("时间格式不正确: "+e.getMessage());
        }
    }

    /**
     * 将分钟数转换为中文表示的时间字符串
     * 例如：90分钟将转换为"1小时30分"，30分钟转换为"0小时30分"
     */
    public static String convertMinutesToChinese(int totalMinutes) {
        Duration duration = Duration.ofMinutes(totalMinutes);
        long hours = duration.toHours();
        long mins = duration.toMinutes() % 60;
        // 格式化确保小时和分钟均为两位数字
        return hours + "小时" + mins + "分";
    }

    /**
     * 将总秒数转换为中文表示的小时和分钟。
     */
    public static String convertSecondsToChinese(int totalSeconds) {
        // 1. 使用总秒数创建一个 Duration 对象
        Duration duration = Duration.ofSeconds(totalSeconds);
        // 2. 从 Duration 中获取小时、分钟和剩余的秒数
        long hours = duration.toHours();
        long mins = duration.toMinutes() % 60;
        long secs = duration.getSeconds() % 60;
        // 3. 检查剩余的秒数，如果超过30秒，则分钟数加一
        if (secs > 10) {
            mins++;
            // 4. 如果分钟数加一后等于60，需要向小时进位
            if (mins == 60) {
                mins = 0;
                hours++;
            }
        }
        // 5. 拼接成中文格式的字符串
        return hours + "小时" + mins + "分钟";
    }
    /**
     * 将总秒数精确转换为分钟数。
     * (例如：143秒 -> 2分钟， 149秒 -> 2分钟)
     * @return 转换后的分钟数
     */
    public static String convertSecondsToMinutesExact(int totalSeconds) {
        // 使用 Duration 是处理时间单位转换的最佳实践，它能正确处理负数
        return Duration.ofSeconds(totalSeconds).toMinutes()+"分钟";
    }

    /**
     * 将总秒数转换为分钟数，并根据秒数进行四舍五入。
     * (例如：143秒 -> 2分钟， 150秒 -> 3分钟)
     * @return 四舍五入后的分钟数
     */
    public static long convertSecondsToMinutesRounded(int totalSeconds) {
        // 使用 Math.round 进行四舍五入是最简洁和清晰的方式
        // 先将秒数转换为double类型的分钟，再进行 rounding
        return Math.round(totalSeconds / 60.0);
    }

    /**
     * 给指定日期添加指定分钟数
     * @return 添加后的新日期
     */
    public static Date queryDate(Date dateTime, int minutes) {
        // 判空处理
        if (dateTime == null) {
            return null;
        }
        // 使用Calendar进行日期计算
        Calendar calendar = Calendar.getInstance();
        // 设置基础日期
        calendar.setTime(dateTime);
        // 添加指定分钟数
        calendar.add(Calendar.MINUTE, minutes);
        // 返回计算后的日期
        return calendar.getTime();
    }

    /**
     * 根据Date类型的生日计算年龄
     */
    public static int calculateAge(Date birthDate) {
        // 处理空值情况
        if (birthDate == null) {
            throw new IllegalArgumentException("生日日期不能为null");
        }
        // 将java.util.Date转换为java.time.LocalDate
        LocalDate birthLocalDate = birthDate.toInstant()
                .atZone(ZoneId.systemDefault())
                .toLocalDate();
        // 获取当前日期
        LocalDate currentDate = LocalDate.now();
        // 计算年龄
        Period period = Period.between(birthLocalDate, currentDate);
        return period.getYears();
    }

    /**
     * 获取月/日 08/08
     */
    public static final String convertDateToMMdd(Date dateTime)
    {
        return DateFormatUtils.format(dateTime, MM_DD);
    }

    /**
     * 获取当天是一周内的星期几
     */
    public static String queryWeekday(Date date) {
        // 创建Calendar实例并设置时间
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        // 获取星期几（1=周日，2=周一，...，7=周六）
        int dayOfWeek = calendar.get(Calendar.DAY_OF_WEEK);
        // 中文星期数组（索引0对应周日，1对应周一...6对应周六）
        String[] chineseWeekdays = {"周日", "周一", "周二", "周三", "周四", "周五", "周六"};
        // 根据星期数值获取对应的中文名称
        return chineseWeekdays[dayOfWeek - 1];
    }

    /**
     * 获取当天是一个月内的第几天
     */
    public static String queryMonthday(Date date) {
        // 创建Calendar实例并设置时间
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        // 获取当月的第几天（1-31）
        return String.valueOf(calendar.get(Calendar.DAY_OF_MONTH));
    }
    /**
     * 通过当天日期返回昨天日期
     */
    public static Date queryYesterday(Date startDate) {
        // 将Date转换为LocalDate（Java 8及以上）
        LocalDate targetDate = startDate.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
        // 如果不是今天，获取上一天日期
        LocalDate yesterday = targetDate.minusDays(1);
        // 将LocalDate转换为Date，设置时间为当天的00:00:00以确保只包含年月日信息
        LocalDateTime yesterdayStart = LocalDateTime.of(yesterday, LocalTime.MIN);
        return Date.from(yesterdayStart.atZone(ZoneId.systemDefault()).toInstant());
    }

    /**
     * 判断两个年月是否相同。
     */
    public static boolean isSameYearMonth(Date date1, Date date2) {
        // 1. 处理 null 情况，如果任一日期为 null，则它们不可能是同一个月
        if (date1 == null || date2 == null) {
            return false;
        }
        // 2. 定义时区，使用系统默认时区
        ZoneId zoneId = ZoneId.systemDefault();
        // 3. 将 Date 转换为 YearMonth 对象
        YearMonth yearMonth1 = YearMonth.from(date1.toInstant().atZone(zoneId));
        YearMonth yearMonth2 = YearMonth.from(date2.toInstant().atZone(zoneId));
        // 4. 比较两个 YearMonth 对象是否相等
        return yearMonth1.equals(yearMonth2);
    }
    /**
     * 判断两个Date对象是否为相同的年月日
     *
     * @param date1 第一个日期
     * @param date2 第二个日期
     * @return 如果是相同年月日返回true，否则返回false；如果有任何一个日期为null则返回false
     */
    public static boolean isSameYearMonthDay(Date date1, Date date2) {
        // 处理null情况
        if (date1 == null || date2 == null) {
            return false;
        }

        // 将Date转换为LocalDate，忽略时间部分
        LocalDate localDate1 = date1.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
        LocalDate localDate2 = date2.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();

        // 比较年月日是否相同
        return localDate1.equals(localDate2);
    }

    /**
     * 判断两个LocalDate对象是否为相同的年月日
     *
     * @param localDate1 第一个本地日期
     * @param localDate2 第二个本地日期
     * @return 如果是相同年月日返回true，否则返回false；如果有任何一个日期为null则返回false
     */
    public static boolean isSameYearMonthDay(LocalDate localDate1, LocalDate localDate2) {
        // 处理null情况
        if (localDate1 == null || localDate2 == null) {
            return false;
        }

        // 比较年月日是否相同
        return localDate1.equals(localDate2);
    }
    /**
     * 判断一个日期是否在另一个日期之后，并且它们的年月不同。
     * 换句话说，判断 date1 是否在 date2 所在月份之后。
     * @return 如果 date1 在 date2 之后，并且两者年月不同，则返回 true；否则返回 false。 如果任一日期为 null，也返回 false。
     */
    public static boolean isAfterAndDifferentMonth(Date date1, Date date2) {
        // 1. 处理 null 情况
        if (date1 == null || date2 == null) {
            return false;
        }
        // 2. 先判断 date1 是否在 date2 之后 使用 Date 的 after() 方法，简单直接
        boolean isAfter = date1.after(date2);
        // 3. 再判断它们的年月是否不同 这里直接复用我们之前创建的 isSameYearMonth 方法
        boolean isDifferentMonth = isSameYearMonth(date1, date2);
        // 4. 返回两个条件的与（AND）结果
        return isAfter || isDifferentMonth;
    }

    /**
     * 通过日期获取天
     */
    public static Integer queryDayOfMonth(Date date) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        return calendar.get(Calendar.DAY_OF_MONTH);
    }

    /**
     * 获取指定日期所在月份的天数。
     * @return 指定月份的天数。
     */
    public static int getDaysInMonth(Date date) {
        // 1. 参数校验：这是一个好习惯，可以防止 NullPointerException
        if (date == null) {
            throw new IllegalArgumentException("输入的日期 (date) 不能为 null。");
        }
        // 2. 创建 Calendar 实例并设置时间
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        // 3. 使用 getActualMaximum 获取当月最大天数
        return calendar.getActualMaximum(Calendar.DAY_OF_MONTH);
    }

    /**
     * 通过天的偏移量获取日期
     * @param date 日期
     * @param monthOffset 天偏移量
     * @return
     */
    public static Date queryMonthOfYear(Date date, Integer monthOffset) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.add(Calendar.DATE, monthOffset);
        return calendar.getTime();
    }
    /**
     * 计算日差
     */
    public static Integer queryDaysDifference(Date startDate, Date endDate) {
        try {
            // 注意：必须指定时区，因为 Date 内部是 UTC 时间戳
            ZoneId zoneId = ZoneId.systemDefault(); // 或者指定特定时区，如 ZoneId.of("Asia/Shanghai")
            // 将 java.util.Date 转换为 java.time.LocalDate
            LocalDate startLocalDate = startDate.toInstant().atZone(zoneId).toLocalDate();
            LocalDate endLocalDate = endDate.toInstant().atZone(zoneId).toLocalDate();
            // 计算日差
            return Convert.toInt(ChronoUnit.DAYS.between(startLocalDate, endLocalDate));
        } catch (Exception e) {
            throw new RuntimeException(e.getMessage());
        }
    }
    /**
     * 计算月差
     */
    public static Integer queryMonthsDifference(Date startDate, Date endDate) {
        try {
            // 注意：必须指定时区，因为 Date 内部是 UTC 时间戳
            ZoneId zoneId = ZoneId.systemDefault(); // 或者指定特定时区，如 ZoneId.of("Asia/Shanghai")
            LocalDate startLocalDate = startDate.toInstant().atZone(zoneId).toLocalDate();
            LocalDate endLocalDate = endDate.toInstant().atZone(zoneId).toLocalDate();
            //计算月差
            return Convert.toInt(ChronoUnit.MONTHS.between(startLocalDate, endLocalDate));
        }catch (Exception e){
            throw new RuntimeException(e.getMessage());
        }
    }
    /**
     * 获取两个之间所有日期的“日”字符串列表。
     * @return 包含“日”字符串的列表，例如 ["06", "07"]
     */
    public static List<String> getDayStringsBetweenDates(Date startDate, Date endDate) {
        // a. 将 java.util.Date 转换为 java.time.LocalDate
        // 注意：需要指定时区，通常使用系统默认时区 ZoneId.systemDefault()
        ZoneId zoneId = ZoneId.systemDefault();
        LocalDate startLocalDate = startDate.toInstant().atZone(zoneId).toLocalDate();
        LocalDate endLocalDate = endDate.toInstant().atZone(zoneId).toLocalDate();

        // b. 使用 Stream API 生成日期序列并格式化
        // datesUntil() 方法会生成从 startLocalDate 到 endLocalDate (包含) 的所有日期流
        return startLocalDate.datesUntil(endLocalDate.plusDays(1)) // 注意：datesUntil 是右开区间，所以 end 需要 +1 天
                .map(date -> date.format(DateTimeFormatter.ofPattern("dd"))) // 格式化日期为 "dd" 格式
                .collect(Collectors.toList()); // 将流收集成 List
    }

    /**
     * 获取两个之间所有日期的“年-月-日”字符串列表。
     */
    public static List<String> getDayStringsBetweenDate(Date startDate, Date endDate) {
        // a. 将 java.util.Date 转换为 java.time.LocalDate
        // 注意：需要指定时区，通常使用系统默认时区 ZoneId.systemDefault()
        ZoneId zoneId = ZoneId.systemDefault();
        LocalDate startLocalDate = startDate.toInstant().atZone(zoneId).toLocalDate();
        LocalDate endLocalDate = endDate.toInstant().atZone(zoneId).toLocalDate();

        // b. 使用 Stream API 生成日期序列并格式化
        // datesUntil() 方法会生成从 startLocalDate 到 endLocalDate (包含) 的所有日期流
        return startLocalDate.datesUntil(endLocalDate.plusDays(1)) // 注意：datesUntil 是右开区间，所以 end 需要 +1 天
                .map(date -> date.format(DateTimeFormatter.ofPattern(YYYY_MM_DD))) // 格式化日期为 "dd" 格式
                .collect(Collectors.toList()); // 将流收集成 List
    }

    /**
     * 日期转中文格式 (带年月日)，例如：2025年10月09日
     *
     * @param date 日期对象
     * @return 中文日期字符串
     */
    public static String dateTimeChinese(Date date) {
        if (date == null) {
            return ""; // 或者返回 null，根据您的业务需求处理
        }
        return DateFormatUtils.format(date, "yyyy年MM月dd日");
    }

    /**
     * 将 "yyyy年MM月dd日" 格式的字符串转换为 Date 对象
     *
     * @param chineseDateStr 中文日期字符串，例如 "2025年10月09日"
     * @return 对应的 Date 对象
     * @throws ParseException 如果输入的字符串格式不正确，则抛出此异常
     */
    public static Date parseChineseDate(String chineseDateStr) {
        try {
            if (chineseDateStr == null || chineseDateStr.trim().isEmpty()) {
                return null;
            }
            // 使用 DateUtils.parseDate 并指定匹配的格式
            // "yyyy年MM月dd日" 这个模式必须与输入字符串完全匹配
            return DateUtils.parseDate(chineseDateStr, "yyyy年MM月dd日");
        }catch (Exception e){
            throw new RuntimeException(e.getMessage());
        }
    }
    /**
     * 将 "yyyy-MM-dd" 格式的字符串转换为 Date 对象
     */
    public static Date parseDate(String dateStr) {
        try {
            if (dateStr == null || dateStr.trim().isEmpty()) {
                return null;
            }
            // 使用 DateUtils.parseDate 并指定匹配的格式
            // "yyyy年MM月dd日" 这个模式必须与输入字符串完全匹配
            return DateUtils.parseDate(dateStr, YYYY_MM_DD);
        }catch (Exception e){
            throw new RuntimeException(e.getMessage());
        }
    }
    /**
     * 将 "yyyy年MM月dd日" 格式的字符串转换为对应的中文星期几。
     *
     * @param chineseDateStr 中文日期字符串，例如 "2025年10月09日"
     * @return 中文星期几，例如 "星期四"。如果输入为null或格式错误，返回null。
     */
    public static String getChineseWeekday(String chineseDateStr) {
        if (chineseDateStr == null || chineseDateStr.trim().isEmpty()) {
            return null;
        }
        Date date = parseChineseDate(chineseDateStr);
        return queryWeekday(date);
    }

    /**
     * 获取指定日期所在周的周日日期 (java.util.Date)。
     * 若输入日期已是周日，则返回该日期。
     *
     * @param date 输入的日期
     * @return 本周的周日 (java.util.Date)
     */
    public static Date getSundayOfWeek(Date date) {
        if (date == null) {
            return null;
        }

        // 1. 将 java.util.Date 转换为 java.time.LocalDate
        LocalDate localDate = date.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();

        // 2. 使用 TemporalAdjusters 找到本周的第一个周日
        LocalDate sunday = localDate.with(TemporalAdjusters.previousOrSame(DayOfWeek.SUNDAY));

        // 3. 将 LocalDate 转换回 java.util.Date
        return Date.from(sunday.atStartOfDay(ZoneId.systemDefault()).toInstant());
    }

    /**
     * 获取指定日期所在周的周六日期 (java.util.Date)。
     * 若输入日期已是周六，则返回该日期。
     *
     * @param date 输入的日期
     * @return 本周的周六 (java.util.Date)
     */
    public static Date getSaturdayOfWeek(Date date) {
        if (date == null) {
            return null;
        }
        // 1. 将 java.util.Date 转换为 java.time.LocalDate
        LocalDate localDate = date.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
        // 2. 使用 TemporalAdjusters 找到本周的最后一个周六
        LocalDate saturday = localDate.with(TemporalAdjusters.nextOrSame(DayOfWeek.SATURDAY));
        // 3. 将 LocalDate 转换回 java.util.Date
        return Date.from(saturday.atStartOfDay(ZoneId.systemDefault()).toInstant());
    }

    /**
     * 获取指定日期所在月份的第一天（月初）日期 (java.util.Date)。
     *
     * @param date 输入的日期
     * @return 本月的第一天 (java.util.Date)
     */
    public static Date getFirstDayOfMonth(Date date) {
        if (date == null) {
            return null;
        }

        // 1. 将 java.util.Date 转换为 java.time.LocalDate
        LocalDate localDate = date.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();

        // 2. 使用 TemporalAdjusters 找到本月的第一天
        LocalDate firstDayOfMonth = localDate.with(TemporalAdjusters.firstDayOfMonth());

        // 3. 将 LocalDate 转换回 java.util.Date
        // atStartOfDay() 会将时间设置为 00:00:00
        return Date.from(firstDayOfMonth.atStartOfDay(ZoneId.systemDefault()).toInstant());
    }

    /**
     * 获取指定日期所在月份的最后一天（月末）日期 (java.util.Date)。
     *
     * @param date 输入的日期
     * @return 本月的最后一天 (java.util.Date)
     */
    public static Date getLastDayOfMonth(Date date) {
        if (date == null) {
            return null;
        }

        // 1. 将 java.util.Date 转换为 java.time.LocalDate
        LocalDate localDate = date.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();

        // 2. 使用 TemporalAdjusters 找到本月的最后一天
        LocalDate lastDayOfMonth = localDate.with(TemporalAdjusters.lastDayOfMonth());

        // 3. 将 LocalDate 转换回 java.util.Date
        // atStartOfDay() 会将时间设置为 00:00:00
        return Date.from(lastDayOfMonth.atStartOfDay(ZoneId.systemDefault()).toInstant());
    }


    /**
     * 根据给定的日期，计算出该日期所在周（周日到周六）的所有日期。
     * 使用传统的 java.util.Calendar API。
     *
     * @param date 给定的日期 (java.util.Date)
     * @return 包含一周日期的 List<Date>，顺序为 [周日, 周一, ..., 周六]
     */
    public static List<Date> getWeekDatesLegacy(Date date) {
        List<Date> weekDates = new ArrayList<>();

        if (date == null) {
            return weekDates;
        }
        Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        // 1. 找到本周的第一个周日
        // Calendar.SUNDAY 的值是 1, MONDAY 是 2, ..., SATURDAY 是 7
        int dayOfWeek = cal.get(Calendar.DAY_OF_WEEK);
        // 计算需要回退的天数：如果今天是周日(1)，回退0天；如果是周一(2)，回退1天...
        int daysToSubtract = dayOfWeek - Calendar.SUNDAY;
        cal.add(Calendar.DATE, -daysToSubtract);

        // 2. 循环7次，添加从周日到周六的日期
        for (int i = 0; i < 7; i++) {
            // 使用 cal.getTime() 的副本，否则 List 中所有元素都会指向同一个可变的 Calendar 对象
            weekDates.add(new Date(cal.getTimeInMillis()));
            cal.add(Calendar.DATE, 1);
        }
        return weekDates;
    }
    /**
     * 根据给定的日期，计算出该日期所在月份的所有日期（从月初到月末）。
     * 使用传统的 java.util.Calendar API。
     *
     * @param date 给定的日期 (java.util.Date)
     * @return 包含一个月所有日期的 List<Date>，顺序为 [1号, 2号, ..., 月末最后一天]
     */
    public static List<Date> getMonthDatesLegacy(Date date) {
        List<Date> monthDates = new ArrayList<>();

        // 如果输入日期为 null，则返回空列表
        if (date == null) {
            return monthDates;
        }

        Calendar cal = Calendar.getInstance();
        cal.setTime(date);

        // 1. 将日历调整到当月的第一天
        cal.set(Calendar.DAY_OF_MONTH, 1);

        // 2. 记录当前月份，用于判断是否循环到了下个月
        int currentMonth = cal.get(Calendar.MONTH);

        // 3. 循环添加日期，直到月份发生变化
        // 当 cal.get(Calendar.MONTH) != currentMonth 时，说明已经到了下个月的第一天，循环结束
        while (cal.get(Calendar.MONTH) == currentMonth) {
            // 关键：添加 Calendar 当前时间的一个副本 (new Date(...))
            // 如果直接 add(cal.getTime())，List 中所有元素会指向同一个可变的 Date 对象
            monthDates.add(new Date(cal.getTimeInMillis()));
            // 将日期向后移动一天
            cal.add(Calendar.DATE, 1);
        }

        return monthDates;
    }

    /**
     * 根据给定的日期，获取包括该日期在内的前7天的所有日期。
     * 使用传统的 java.util.Calendar API。
     *
     * @param date 给定的日期 (java.util.Date)
     * @return 包含7个日期的 List<Date>，顺序为 [给定日期, 前一天, ..., 前6天]
     */
    public static List<Date> getPreviousSevenDays(Date date) {
        List<Date> previousSevenDays = new ArrayList<>();
        // 如果输入日期为 null，则返回空列表
        if (date == null) {
            return previousSevenDays;
        }
        // 使用 Calendar 进行日期计算
        Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        // 循环7次，从给定日期开始，依次向前推算并添加日期
        for (int i = 0; i < 7; i++) {
            // 1. 添加当前 Calendar 所代表的日期的副本
            //    必须使用 new Date(cal.getTimeInMillis()) 创建新对象，
            //    否则 List 中所有元素都会指向同一个 Calendar 内部的时间戳，
            //    最终所有元素都会是循环结束后的那个日期。
            previousSevenDays.add(new Date(cal.getTimeInMillis()));
            // 2. 将 Calendar 的日期向前推进一天（即日期减1）
            cal.add(Calendar.DATE, -1);
        }
        return previousSevenDays;
    }

    public static void main(String[] args) throws ParseException {
//        // 假设今天是 2023年10月27日
//        SimpleDateFormat sdfInput = new SimpleDateFormat("yyyy-MM-dd");
//        Date startUtilDate = sdfInput.parse("2025-09-06");
//        List<Date> last7Days = DateUtils.getPreviousSevenDays(startUtilDate);
//        last7Days = last7Days.stream().sorted().collect(Collectors.toList());
//        // 使用 SimpleDateFormat 来格式化日期，方便查看
//        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
//
//        System.out.println("获取的日期列表 (从今天开始，往前7天):");
//        for (Date d : last7Days) {
//            System.out.println(sdf.format(d));
//        }
//        System.out.println(getMonthDatesLegacy(new Date()));
        System.out.println(getWeekDatesLegacy(new Date()));
    }

    /**
     * 获取指定 java.util.Date 所在周的开始日期 (周日) (返回 java.util.Date)
     *
     * @param date   指定日期 (java.util.Date)
     * @param zoneId 时区，用于正确计算日期。例如 ZoneId.systemDefault()
     * @return 周的开始日期 (周日) (java.util.Date)，时间部分为 00:00:00
     */
    public static Date getWeekStart(Date date, ZoneId zoneId) {
        if (date == null || zoneId == null) {
            return null;
        }

        // 1. 将 java.util.Date 转换为 java.time.LocalDate
        LocalDate localDate = date.toInstant().atZone(zoneId).toLocalDate();

        // 2. 获取本周的第一个周日
        //    TemporalAdjusters.previousOrSame(DayOfWeek.SUNDAY) 会找到当前日期或之前最近的一个周日
        LocalDate firstDayOfWeek = localDate.with(TemporalAdjusters.previousOrSame(DayOfWeek.SUNDAY));

        // 3. 将 LocalDate 转换回 java.util.Date (默认时间为 00:00:00)
        return Date.from(firstDayOfWeek.atStartOfDay(zoneId).toInstant());
    }

    /**
     * 获取指定 java.util.Date 所在周的结束日期 (周六) (返回 java.util.Date)
     *
     * @param date   指定日期 (java.util.Date)
     * @param zoneId 时区，用于正确计算日期。例如 ZoneId.systemDefault()
     * @return 周的结束日期 (周六) (java.util.Date)，时间部分为 23:59:59.999
     */
    public static Date getWeekEnd(Date date, ZoneId zoneId) {
        if (date == null || zoneId == null) {
            return null;
        }

        // 1. 将 java.util.Date 转换为 java.time.LocalDate
        LocalDate localDate = date.toInstant().atZone(zoneId).toLocalDate();

        // 2. 获取本周的最后一个周六
        //    TemporalAdjusters.nextOrSame(DayOfWeek.SATURDAY) 会找到当前日期或之后最近的一个周六
        LocalDate lastDayOfWeek = localDate.with(TemporalAdjusters.nextOrSame(DayOfWeek.SATURDAY));

        // 3. 将 LocalDate 转换回 java.util.Date，并将时间设置为 23:59:59.999
        return Date.from(lastDayOfWeek.atTime(23, 59, 59, 999_000_000).atZone(zoneId).toInstant());
    }

    /**
     * 获取指定 java.util.Date 所在月份的月初日期 (返回 java.util.Date)
     *
     * @param date   指定日期 (java.util.Date)
     * @param zoneId 时区，用于正确计算日期。例如 ZoneId.systemDefault()
     * @return 月初日期 (java.util.Date)，时间部分为 00:00:00
     */
    public static Date getMonthStart(Date date, ZoneId zoneId) {
        if (date == null || zoneId == null) {
            return null;
        }

        // 1. 将 java.util.Date 转换为 java.time.LocalDate
        LocalDate localDate = date.toInstant().atZone(zoneId).toLocalDate();

        // 2. 获取本月的第一天
        LocalDate firstDayOfMonth = localDate.withDayOfMonth(1);

        // 3. 将 LocalDate 转换回 java.util.Date (默认时间为 00:00:00)
        return Date.from(firstDayOfMonth.atStartOfDay(zoneId).toInstant());
    }

    /**
     * 获取指定 java.util.Date 所在月份的月末日期 (返回 java.util.Date)
     *
     * @param date   指定日期 (java.util.Date)
     * @param zoneId 时区，用于正确计算日期。例如 ZoneId.systemDefault()
     * @return 月末日期 (java.util.Date)，时间部分为 23:59:59.999
     */
    public static Date getMonthEnd(Date date, ZoneId zoneId) {
        if (date == null || zoneId == null) {
            return null;
        }

        // 1. 将 java.util.Date 转换为 java.time.LocalDate
        LocalDate localDate = date.toInstant().atZone(zoneId).toLocalDate();

        // 2. 获取本月的最后一天
        LocalDate lastDayOfMonth = localDate.with(TemporalAdjusters.lastDayOfMonth());

        // 3. 将 LocalDate 转换回 java.util.Date，并将时间设置为 23:59:59.999
        return Date.from(lastDayOfMonth.atTime(23, 59, 59, 999_000_000).atZone(zoneId).toInstant());
    }

    /**
     * 计算指定日期多年后的日期
     *
     * @param originalDate 原始日期
     * @param years 要增加的年数（可以是正数或负数）
     * @return 计算后的日期
     */
    public static Date addYears(Date originalDate, int years) {
        // 空值校验
        if (originalDate == null) {
            throw new IllegalArgumentException("原始日期不能为null");
        }

        // 将Date转换为LocalDate并增加指定年数
        LocalDate localDate = originalDate.toInstant()
                .atZone(ZoneId.systemDefault())
                .toLocalDate()
                .plusYears(years);

        // 将LocalDate转换回Date
        return Date.from(localDate.atStartOfDay(ZoneId.systemDefault()).toInstant());
    }

    /**
     * 两个时间相减算出分钟差额
     * @param startDate
     * @param endDate
     * @return
     */
    public static long getMinutesBetweenDates(Date startDate, Date endDate) {
        // 检查日期是否为null
        if (startDate == null || endDate == null) {
            throw new IllegalArgumentException("日期不能为null");
        }

        // 获取两个日期的毫秒数
        long startTime = startDate.getTime();
        long endTime = endDate.getTime();

        // 计算差值(毫秒)并转换为分钟
        long diffInMillies = Math.abs(endTime - startTime);
        return diffInMillies / (60 * 1000);
    }
    // 判断两个Date是否是同一年月
    public static boolean isSameYearAndMonth(Date date1, Date date2) {
        if (date1 == null || date2 == null) {
            return false; // 处理null情况
        }

        Calendar cal1 = Calendar.getInstance();
        Calendar cal2 = Calendar.getInstance();

        cal1.setTime(date1);
        cal2.setTime(date2);

        // 比较年份和月份
        return cal1.get(Calendar.YEAR) == cal2.get(Calendar.YEAR) &&
                cal1.get(Calendar.MONTH) == cal2.get(Calendar.MONTH);
    }
}
